#include "janna/libs/extensionsystem/plugin_manager.h"

#include <QPluginLoader>

#include "janna/libs/utils/json_util.h"
#include "janna/libs/utils/log_util.h"
#include "janna/libs/utils/pubsub.h"
namespace janna {

QString PluginItem::id() const {
    return this->m_id;
}
void PluginItem::id(QString &&id) {
    this->m_id = std::move(id);
}

QString PluginItem::version() const {
    return this->m_version;
}
void PluginItem::version(QString &&version) {
    this->m_version = std::move(version);
}

QString PluginItem::path() const {
    return this->m_path;
}
void PluginItem::path(QString &&path) {
    this->m_path = std::move(path);
}

bool PluginItem::enable() const {
    return this->m_enable;
}
void PluginItem::setEnable(bool enable) {
    this->m_enable = enable;
}

Q_GLOBAL_STATIC(PluginManager, pluginManager)

PluginManager::PluginManager() {
    this->log = LogUtil::getLogger("PluginManager");
    connect(Pubsub::getInstance(), &Pubsub::signalPublish, this, &PluginManager::subscribe);
}

PluginManager *PluginManager::getInstance() {
    return pluginManager();
}

void PluginManager::loadPlugins(const QString &pluginPath) {
    auto pluginConfigPath = pluginPath + "/plugins.json";
    auto pluginItems      = this->parsePluginItems(pluginConfigPath);
    if (pluginItems.isEmpty()) {
        SPDLOG_LOGGER_ERROR(this->log, fmt::format("插件配置为空. {}", pluginConfigPath.toStdString()));
        return;
    }

    for (auto i = 0; i < pluginItems.count(); i++) {
        const auto &item = pluginItems.at(i);
        // 如果差价没有启用,则跳过
        if (!item->enable()) {
            continue;
        }

        // 加载插件
        SPDLOG_LOGGER_DEBUG(this->log, fmt::format("load plugin:{}.{}", item->id().toStdString(), item->version().toStdString()));
        auto currPluginPath = pluginPath + "/" + item->path();
        SPDLOG_LOGGER_DEBUG(this->log, fmt::format("plugin path:{}", currPluginPath.toStdString()));
        if (this->loadSinglePlugin(currPluginPath, item)) {
            SPDLOG_LOGGER_DEBUG(this->log,
                                fmt::format("load plugin:{}.{} success", item->id().toStdString(), item->version().toStdString()));
        } else {
            SPDLOG_LOGGER_DEBUG(this->log,
                                fmt::format("load plugin:{}.{} failed", item->id().toStdString(), item->version().toStdString()));
        }
    }
}

QList<PluginItem *> PluginManager::parsePluginItems(const QString &pluginConfigPath) {
    QList<PluginItem *> items;
    auto                pluginsDoc = JsonUtil::readJsonAsDocument(pluginConfigPath);
    if (!pluginsDoc) {
        SPDLOG_LOGGER_ERROR(this->log, fmt::format("插件配置解析失败. {}", pluginConfigPath.toStdString()));
        return items;
    }
    if (!JsonUtil::validateJson(pluginConfigPath, AppConfig::getInstance()->pluginListJsonSchemaPath())) {
        SPDLOG_LOGGER_ERROR(this->log, fmt::format("插件配置验证失败. {}", pluginConfigPath.toStdString()));
        return items;
    }

    if (!pluginsDoc->IsArray()) {
        SPDLOG_LOGGER_ERROR(this->log, fmt::format("插件配置格式不正确. {}", pluginConfigPath.toStdString()));
        return items;
    }
    auto array = pluginsDoc->GetArray();
    for (rapidjson::SizeType i = 0; i < array.Size(); i++) {
        auto item = new PluginItem();
        items.append(item);
        auto &el = array[i];

        auto id = rapidjson::Pointer("/id").Get(el);
        if (id && id->IsString()) {
            item->id(id->GetString());
        }

        auto version = rapidjson::Pointer("/version").Get(el);
        if (version && version->IsString()) {
            item->version(version->GetString());
        }

        auto path = rapidjson::Pointer("/path").Get(el);
        if (path && path->IsString()) {
            item->path(path->GetString());
        }

        auto enable = rapidjson::Pointer("/enable").Get(el);
        if (enable && enable->IsBool()) {
            item->setEnable(enable->GetBool());
        }
    }

    return items;
}

bool PluginManager::loadSinglePlugin(const QString &pluginPath, PluginItem *item) {
    bool currPluginLoadSuccess = false;
    // 加载插件
    QDir              pluginsDir(pluginPath);
    const QStringList entries = pluginsDir.entryList(QDir::Files);
    for (const QString &fileName : entries) {
        // 只处理 .so 文件
        if (!fileName.endsWith(".so")) {
            continue;
        }
        QPluginLoader pluginLoader(pluginsDir.absoluteFilePath(fileName));
        QObject      *plugin = pluginLoader.instance();

        if (!plugin) {
            SPDLOG_LOGGER_ERROR(this->log, fmt::format("插件加载失败. 插件:{}", item->id().toStdString()));
            SPDLOG_LOGGER_ERROR(this->log, fmt::format("插件加载失败原因:{}", pluginLoader.errorString().toStdString()));
            pluginLoader.unload();
            break;
        }

        JannaPluginInterface *currPlugin = nullptr;
        currPlugin                       = qobject_cast<JannaPluginInterface *>(plugin);
        if (!currPlugin) {
            SPDLOG_LOGGER_ERROR(this->log, fmt::format("插件加载失败. 插件:{}", currPlugin->pluginName().toStdString()));
            pluginLoader.unload();
            break;
        }

        if (!this->parseMetadata(currPlugin, pluginPath, item)) {
            SPDLOG_LOGGER_ERROR(this->log, fmt::format("插件元数据解析失败. 插件:{}", currPlugin->pluginName().toStdString()));
            pluginLoader.unload();
            break;
        }

        currPluginLoadSuccess = true;
        this->m_plugin_map.insert(currPlugin->pluginName(), currPlugin);
    }
    return currPluginLoadSuccess;
}
bool PluginManager::parseMetadata(JannaPluginInterface *currPlugin, const QString &pluginPath, PluginItem *item) {
    auto jsonFilePath = pluginPath + "/" + item->id() + ".json";
    SPDLOG_LOGGER_INFO(this->log, fmt::format("插件配置文件:{}", jsonFilePath.toStdString()));
    auto jsonDoc = JsonUtil::readJsonAsDocument(jsonFilePath);
    if (!jsonDoc) {
        SPDLOG_LOGGER_ERROR(this->log, fmt::format("插件配置解析失败. {}", pluginPath.toStdString()));
        return false;
    }

    if (!JsonUtil::validateJson(jsonFilePath, AppConfig::getInstance()->pluginDescJsonSchemaPath())) {
        SPDLOG_LOGGER_ERROR(this->log, fmt::format("插件配置验证解析失败. {}", pluginPath.toStdString()));
        return false;
    }

    if (!jsonDoc->IsObject()) {
        SPDLOG_LOGGER_ERROR(this->log, fmt::format("插件配置格式不正确. {}", pluginPath.toStdString()));
        return false;
    }
    auto metadata = std::make_unique<PluginMetadata>();

    auto id = rapidjson::Pointer("/id").Get(*jsonDoc);
    if (id && id->IsString()) {
        metadata->id(id->GetString());
    }

    auto version = rapidjson::Pointer("/version").Get(*jsonDoc);
    if (version && version->IsString()) {
        metadata->version(version->GetString());
    }

    auto author = rapidjson::Pointer("/author").Get(*jsonDoc);
    if (author && author->IsString()) {
        metadata->author(author->GetString());
    }

    auto name = rapidjson::Pointer("/name").Get(*jsonDoc);
    if (name && name->IsString()) {
        metadata->name(name->GetString());
    }

    auto subscribes = rapidjson::Pointer("/subscribe").Get(*jsonDoc);
    if (!subscribes || !subscribes->IsArray()) {
        SPDLOG_LOGGER_ERROR(this->log, fmt::format("插件配置格式不正确. {}", pluginPath.toStdString()));
        return false;
    }
    QList<JannaTopic> points;
    auto              array = subscribes->GetArray();
    for (rapidjson::SizeType i = 0; i < array.Size(); i++) {
        auto subscribe = new PluginMetadataSubscribe;
        metadata->subscribes().append(subscribe);
        JannaTopic point;

        auto &el     = array[i];
        auto  source = rapidjson::Pointer("/source").Get(el);
        if (source && source->IsString()) {
            subscribe->source(source->GetString());
            point.source = source->GetString();
        }

        auto topic = rapidjson::Pointer("/topic").Get(el);
        if (topic && topic->IsString()) {
            subscribe->topic(topic->GetString());
            point.topic = topic->GetString();
        }

        auto publish = rapidjson::Pointer("/publish").Get(el);
        if (publish && publish->IsString()) {
            subscribe->publish(publish->GetString());
            point.publish = publish->GetString();
        }
        points.append(point);
    }
    currPlugin->initPluginMetadata(metadata.get());
    currPlugin->initPoints(std::move(points));
    auto errorMessage = new QString();
    if (!currPlugin->initialize(errorMessage)) {
        SPDLOG_LOGGER_ERROR(this->log, fmt::format("插件初始化失败. {}", errorMessage->toStdString()));
    }
    return true;
}

void PluginManager::subscribe(const JannaTopic &topic, const QMap<QString, QVariant> &param) {
    QMapIterator<QString, JannaPluginInterface *> it(this->m_plugin_map);
    while (it.hasNext()) {
        it.next();
        it.value()->pointHandlerInterceptor(topic, param);
    }
}
};  // namespace janna